﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Cjc.Calculation.ExpressionEngine;


namespace ExcelExpressionUnitTests
{
    [TestClass]
    public class ExpressionTests
    {
        Engine engine = new Engine();

        [TestMethod]
        public void Arithmetic()
        {
            Worksheet worksheet = engine.CreateWorksheet();

            object res = worksheet.Evaluate("+1");
            Assert.AreEqual(1, res);
            res = worksheet.Evaluate("-2");
            Assert.AreEqual(-2, res);
            // TODO: unary minus
            //res = worksheet.Evaluate("--3");
            //Assert.AreEqual(3, res);

            res = worksheet.Evaluate("1+1");
            Assert.AreEqual(2, res);
            res = worksheet.Evaluate("2-1");
            Assert.AreEqual(1, res);
            res = worksheet.Evaluate("4*2");
            Assert.AreEqual(8, res);
            res = worksheet.Evaluate("4/2");
            Assert.AreEqual(2m, res);

            res = worksheet.Evaluate("2.0+1");
            Assert.AreEqual(3m, res);
            res = worksheet.Evaluate("1+2.0");
            Assert.AreEqual(3m, res);

            res = worksheet.Evaluate("3*2+1");
            Assert.AreEqual(7, res);
            res = worksheet.Evaluate("3*(2+1)");
            Assert.AreEqual(9, res);
            res = worksheet.Evaluate("4/(2.0+2)");
            Assert.AreEqual(1m, res);
        }

        [TestMethod]
        public void Comparison()
        {
            Worksheet worksheet = engine.CreateWorksheet();

            object res = worksheet.Evaluate("1=1");
            Assert.AreEqual(true, res);
            res = worksheet.Evaluate("2==3");
            Assert.AreEqual(false, res);
            res = worksheet.Evaluate("5<>4");
            Assert.AreEqual(true, res);
            res = worksheet.Evaluate("4!=4");
            Assert.AreEqual(false, res);
            res = worksheet.Evaluate("1.0>1");
            Assert.AreEqual(false, res);
            res = worksheet.Evaluate("1.0>=1");
            Assert.AreEqual(true, res);
            res = worksheet.Evaluate("2/2<=1");
            Assert.AreEqual(true, res);
            res = worksheet.Evaluate("1/2<1+1");
            Assert.AreEqual(true, res);
        }

        [TestMethod]
        public void Boolean()
        {
            Worksheet worksheet = engine.CreateWorksheet();

            object res = worksheet.Evaluate("0+true");
            Assert.AreEqual(1, res);
            res = worksheet.Evaluate("42.0*true");
            Assert.AreEqual(42m, res);
            res = worksheet.Evaluate("if(false,1,2)");
            Assert.AreEqual(2, res);
            res = worksheet.Evaluate("if(false+1,1,2)");
            Assert.AreEqual(1, res);
            res = worksheet.Evaluate("if(99,if(99.0,1,0),0)");
            Assert.AreEqual(1, res);
        }

        [TestMethod]
        public void String()
        {
            Worksheet worksheet = engine.CreateWorksheet();

            object res = worksheet.Evaluate("Hello");
            Assert.AreEqual(null, res);
            res = worksheet.Evaluate("\"Hello\"");
            Assert.AreEqual("Hello", res);
            res = worksheet.Evaluate("\"Hello\" + \" \" + \"world\"");
            Assert.AreEqual("Hello world", res);
            // TODO get & working.  + is an alternative for now
            //res = worksheet.Evaluate("\"Hello\" & \" \" & \"world\"");
            //Assert.AreEqual("Hello world", res);
            res = worksheet.Evaluate("\"Hello \" + 1");
            Assert.AreEqual("Hello 1", res);
            res = worksheet.Evaluate("\"Hello \" + 1.0");
            Assert.AreEqual("Hello 1", res);
            res = worksheet.Evaluate("\"Hello\" = \"Hello\"");
            Assert.AreEqual(true, res);
            res = worksheet.Evaluate("\"Hello\" = \"Goodbye\"");
            Assert.AreEqual(false, res);
            res = worksheet.Evaluate("\"a\" < \"b\"");
            Assert.AreEqual(true, res);
            // TODO Excel would return 21
            res = worksheet.Evaluate("20 + \"1\"");
            Assert.AreEqual("201", res);
        }

        [TestMethod]
        public void Functions()
        {
            Worksheet worksheet = engine.CreateWorksheet();

            object res = worksheet.Evaluate("exp(1-1)");
            Assert.AreEqual(1d, res);
            res = worksheet.Evaluate("abs(-2)");
            Assert.AreEqual(2m, res);
            res = worksheet.Evaluate("pow(-2,2)");
            Assert.AreEqual(4d, res);
            res = worksheet.Evaluate("today()");
            Assert.AreEqual(DateTime.Today, res);
            res = worksheet.Evaluate("min(1.1,4/2,true+true)");
            Assert.AreEqual(1.1m, res);
            res = worksheet.Evaluate("max(1.1,4/2,true+true)");
            Assert.AreEqual(2m, res);
            res = worksheet.Evaluate("Round(2.015,2)");
            Assert.AreEqual(2.02m, res);
            res = worksheet.Evaluate("RoundDown(2015.1595,2)");
            Assert.AreEqual(2015.15d, res);
            res = worksheet.Evaluate("RoundUp(2015.1415,1)");
            Assert.AreEqual(2015.2d, res);
            res = worksheet.Evaluate("and(true,2>1,false+1)");
            Assert.AreEqual(true, res);
            res = worksheet.Evaluate("or(true-1,2<=1,false)");
            Assert.AreEqual(false, res);
            res = worksheet.Evaluate("lefT(\"hello\", 4)");
            Assert.AreEqual("hell", res);
            res = worksheet.Evaluate("RIGHT(\"hello\", 1)");
            Assert.AreEqual("o", res);
            res = worksheet.Evaluate("mid(\"hello\", 3, 2)");
            Assert.AreEqual("ll", res);
            res = worksheet.Evaluate("mid(123,2,1)");
            Assert.AreEqual("2", res);
            res = worksheet.Evaluate("Sum(1,0)");
            Assert.AreEqual(1m, res);
            res = worksheet.Evaluate("Sum(1,if(true,9.0,0),3<4)");
            Assert.AreEqual(11m, res);
            res = worksheet.Evaluate("search(\"ll\", \"hello\")");
            Assert.AreEqual(3, res);
            res = worksheet.Evaluate("search(\"xx\", \"hello\")");
            Assert.AreEqual(null, res);
            res = worksheet.Evaluate("search(\"ll\", \"helloll\", 4)");
            Assert.AreEqual(6, res);
            res = worksheet.Evaluate("dump()");
            Assert.AreEqual("", res);
            res = worksheet.Evaluate("dump(false)");
            Assert.AreEqual("", res);
            res = worksheet.Evaluate("dump(true)");
            Assert.AreEqual("", res);
            res = worksheet.Evaluate("A1=1");
            Assert.AreEqual(1, res);
            res = worksheet.Evaluate("dump()");
            Assert.AreEqual("A1 = 1", res);
        }

        [TestMethod]
        public void Cells()
        {
            Worksheet worksheet = engine.CreateWorksheet();

            object res = worksheet.Evaluate("A1=1");
            Assert.AreEqual(1, res);
            res = worksheet.Evaluate("A1");
            Assert.AreEqual(1, res);
            res = worksheet.Evaluate("a1");
            Assert.AreEqual(1, res);
            res = worksheet.Evaluate("A2=2.0");
            Assert.AreEqual(2m, res);
            res = worksheet.Evaluate("A2+a1");
            Assert.AreEqual(3m, res);
            res = worksheet.Evaluate("a3=\"=A2+a1\"");
            Assert.AreEqual("=A2+a1", res);
            res = worksheet.Evaluate("A3");
            Assert.AreEqual(3m, res);
            res = worksheet.Evaluate("A2=8.0");
            Assert.AreEqual(8m, res);
            res = worksheet.Evaluate("A3");
            Assert.AreEqual(9m, res);
            res = worksheet.Evaluate("a4=\"=A3/a1\"");
            Assert.AreEqual("=A3/a1", res);
            res = worksheet.Evaluate("a4");
            Assert.AreEqual(9m, res);
            res = worksheet.Evaluate("A1=2");
            Assert.AreEqual(2, res);
            res = worksheet.Evaluate("A4");
            Assert.AreEqual(5m, res);
            res = worksheet.Evaluate("a3=\"=A2-a1\"");
            Assert.AreEqual("=A2-a1", res);
            res = worksheet.Evaluate("A4");
            Assert.AreEqual(3m, res);
            res = worksheet.Evaluate("a3=7");
            Assert.AreEqual(7, res);
            res = worksheet.Evaluate("A4");
            Assert.AreEqual(3.5m, res);
        }
    }
}
